<?php
/**
 * Created by PhpStorm.
 * User: Happy
 * Date: 2016/5/12 0012
 * Time: 16:58
 *
 * 模拟js实现php的插件机制，实现插件机制的目的是代码复用，能够将常见的数据处理模块化，组件化执行
 * 整个页面逻辑就是数据流的机制，仿照js的事件机制原理
 */
//类似于jquery ,能够创建一个插件对象，包含流程默认的一些事件
//美容院自定义表格组件,类似wordpress自定义组件
class Plugin2{
    protected   $config=array();//组件基本配置 使用子组件的配置
    protected   $Hooks=array();//钩子函数，用于存储绑定的事件等

    //参数传递当前加载子插件的路径
    function __construct($pluginfile='') {
        lib('Model2');//使用nosql需要加载组件,构建aql
        $plugin_path=dirname($pluginfile);
        $config_path=$plugin_path.'/config.php';

             if(file_exists( $config_path)){
                $config=include_once($config_path);
                 if(is_array($config)){
                     $this->config=$config;//加载默认的配置文件
                 }
             }
        $this->config['plugin_path']=$plugin_path;
    }
   //获取绑定的事件和消息
    public  function  get_hook(){
         return $this->Hooks;
    }
   //绑定事件
    public  function  on($event,$listener){
        $Hook=$this->Hooks;
        if(!isset($Hook[$event])){ //如果之前没有绑定
            $Hook[$event]=array();//赋值一个空数组
        }
        $index=array_push($Hook[$event],$listener)-1;//返回事件的索引
        $this->Hooks=$Hook;
        return  array('event'=>$event,'index'=>$index,'listener'=>$listener);

    }
    //解除绑定事件 参数为on事件的返回值
    public  function  off($setting=array()){
           $event=$setting['event'];
           $index=$setting['index'];
         unset ($this->Hooks[$event][$index]);
    }
   //触发事件 最多可以传递两个参数
    public  function  trigger($event,$param1=null,$param2=null){

        if(isset($this->Hooks[$event])){
            $listener=$this->Hooks[$event];
            foreach($listener as $value){
                //有可能$value是数组，
                if(is_array($value)){  //是数组的组件
                   //实例化plugin
                    foreach($value as $key2=>$value2){
                        if(strpos($value2,'->')>0){ //如果存在箭头说明也是触发函数
                            $arr=explode('->',$value2);
                            $key2=trim($arr[0]);
                            $value2=trim($arr[1]);
                        }

                        //触发
                         $plugin= plugin($key2);
                         $plugin->trigger('on_'.$value2,$param1,$param2); //数执行前触发函
                         $return=$plugin->$value2($param1,$param2);
                         $plugin->trigger('end_'.$value2,$return,$param1); //返回值传入结束函数中，函数执行后触发
                    }
                    //触发函数
                }
                else{  //是匿名函数的事件绑定
                    $value($param1,$param2);  //直接执行按照添加顺序监听的代码
                }

            }
        }
    }
    //触发自身的实际函数
    public  function  trigger_method($method,$param1=null,$param2=null){
           $this->$method($param1=null,$param2=null);
    }
    //子类组件公用函数
    //处理配置 用户基础配置与传入的配置或者参数合并
    protected   function  _config($config){
        if($config){
            $config=array_merge($this->config,$config);//新配置会覆盖默认的配置
            $this->config=$config;
        }
        else{
            $config=$this->config;
        }
        return $config;
    }
    //获取当前插件的配置
    public   function  c($key='',$value=null){
        if($value!==null){//设置值
            $this->config[$key]=$value;
        }else{
            //读取值
            if($key){
                return  $this->config[$key];
            }
            else{
                return  $this->config;
            }
        }
    }

    //加载类库
    public function  lib($libs){
        static $lib;
        $path=$this->c('plugin_path');
        if(is_array($libs)){ //如果是数组遍历
            foreach($libs as $value){
                if($lib[$value]){continue;}
                else{
                    $lib[$value]= include($path.'/lib/'.$value.'.php');
                }
            }
            return $lib;
        }
        else{ //加载单个文件
            if(!$lib[$libs]){ $lib[$libs]=include($path.'/lib/'.$libs.'.php');}
            return $lib[$libs];
        }
    }

    //快速操作
    public  function  get_api(){
        $name=$_POST['name'];
        if($name){
            $get_name='get_'.$name;
            if(method_exists($this,$get_name)){
                //获取操作人信息  //获取编辑人员名称
              return $this->$get_name();
            }
            else{
                return array('flag'=>false,'msg'=>'您修改的方法不存在');
            }
        }else{
               return  array('flag'=>false,'msg'=>'错误的参数');
        }
    }
    //显示快速操作
    public  function  set_api(){
        $name=$_POST['name'];
        if($name){
            $set_name='set_'.$name;
            if(method_exists($this,$set_name)){
                //获取操作人信息  //获取编辑人员名称
              return $this->$set_name();
            }
            else{
               return  array('flag'=>false,'msg'=>'美容组件提示:您修改的方法不存在');
            }
        }else{
               return  array('flag'=>false,'msg'=>'美容组件提示:缺少name参数');
        }
    }

   //查询
    public  function  query($sql,$is_ajax=false){
        $result=$this->get_remote(array('sql'=>$sql));
        if(!$result['flag']){
            if($is_ajax){
                $this->send_data(array('flag'=>false,'msg'=>$result['msg']));
            }else{
                exit($result['msg']);
            }

        }
        return $result['data'];
    }
    public  function  execute($sql){
       $result=$this->set_remote(array('sql'=>$sql));
        if(!$result['flag']){$this->send_data($result);}
        return $result['flag'];
    }
    public  function  get_remote($param=array()){
        static $db_name='';
        if(!$db_name){
            $db_name=Model('setting')->get_setting('attach_database');
            if(!$db_name){return array('flag'=>false,'msg'=>"没有设置crm数据库");}
        }
        //
        $param['db_name']=$db_name;
        $param['act']='api_crm';
        if(!$param['name']){//执行sql语句并返回数据
            $param['name']='query';
        }
        if(!$param['collection_name']){$param['collection_name']=randomString(6);}//随机生成一个以通过服务器检测,真正的隐含在sql中或者设置了
        $param['meirong_id']=Model('meirong')->get_id();
        return Model('meirong')->get($param);
    }

     //插入删除更新等操作
    public  function  set_remote($param=array()){
        static $db_name='';
        if(!$db_name){
            $db_name=Model('setting')->get_setting('attach_database');
            if(!$db_name){return array('flag'=>false,'msg'=>"没有设置crm数据库");}
        }
        //
        $param['db_name']=$db_name;
        $param['act']='api_crm';
        if(!$param['name']){//执行sql语句并返回数据
            $param['name']='execute';
        }
        if(!$param['collection_name']){$param['collection_name']=randomString(6);}//随机生成一个以通过服务器检测,真正的隐含在sql中或者设置了
        $param['meirong_id']=Model('meirong')->get_id();
        return Model('meirong')->set($param);
    }

    //handle_month_condition
    //获取redis缓存
    protected  function get_redis($key){
        $db_arr=c('db');
        $db_name=$db_arr['master']['dbname'];
        $data= redis2::get($db_name.':'.$key);
        if($data){
            $first=substr($data,0,1);
            if($first=='['||$first=='{'){
                return json_decode($data,true);
            }
            else{//直接返回字符串
                return $data;
            }
        }
        else{
            return array();
        }
    }
    //设置redis缓存
    protected  function  set_redis($key,$value=array(),$timeout=3600){
        if(is_array($value)){
            $value=json_encode($value);
        }
        $db_arr=c('db');
        $db_name=$db_arr['master']['dbname'];
        return redis2::set($db_name.':'.$key,$value,$timeout);
    }


    public  function  include_file($file_name,$setting=array()){
        $plugin_path=$this->config['plugin_path'];
        $T=new BlitzPhp(); //时间花销很小只有0.3ms左右
        return  $T->Binclude($plugin_path.'/views/'.$file_name.'.html',$setting);
    }
    public  function  show_tab($tab_tpl,$default_part='',$tab_setting=array()){
        //获取基本信息
        $content=$default_part;
        if($_GET['content']){
            $content=$_GET['content'];
        }
        if(method_exists($this,$content)){
            $main=$this->$content();
            $setting['main']=$main;
            $setting['content']=$content;
            if($tab_setting){
                $setting=array_merge($setting,$tab_setting);
            }
        }
        else{
            return '[plugin2-show_tab组件调用]您查找的信息不存在!';
        }
        //根据设置获取不同的模板，比如精简系统复杂系统等
        $main=$this->include_file($tab_tpl, $setting);//根据模板名称获取不同位置的模板
        //如果是异步方式直接获取
        return $main;
    }
      public  function  display($main){
          echo $main;
          exit(0);
      }
    //快速操作
    protected  function  fast_op(){

        $name=$_POST['name'];
        if($name){
            $set_name='set_'.$name;
            if(method_exists($this,$set_name)){
                //获取操作人信息  //获取编辑人员名称
                $result=$this->$set_name();
                $this->send_data($result);//保存编辑
            }
            else{
                $this->send_data(array('flag'=>false,'msg'=>'您修改的方法不存在'.$name));
            }
        }else{
            $this->send_data(array('flag'=>false,'msg'=>'错误的参数,缺少name'));
        }
    }

    public function  send_data($data=array()){
        exit(json_encode($data));
    }

    //检查数据字段是否在数据字典中,如果不在就返回错误,防止插入类似flag msg这样的数据或者插入错误表格
    public  function   check($collection='',$data=array(),$filter=array()){
        if(!$collection){send_data(array('flag'=>false,'msg'=>'no collection'));}
        if(!$data){send_data(array('flag'=>false,'msg'=>'no data'));}

        static $prop_arr;
        if(!$prop_arr){ //防止批量插入多次查询
            $field_arr=Model3('table_column')->field('prop_name')->where(array('collection_name'=>$collection))->select();
            $prop_arr=array();
            foreach($field_arr as $value){
                $prop_arr[]=$value['prop_name'];
            }
        }

        foreach($data as $key=> $value){

            if(!in_array($key,$prop_arr)){

                if(c('debug')){//调试模式
                    send_data(array('flag'=>false,'msg'=>'错误:collection: ['.$collection.'] not have field 【'.$key.'】'));
                }
                else{
                    throw new Exception('collection: ['.$collection.'] not have field 【'.$key.'】');
                }
            }
            else if($key=='add_time'&&!is_numeric($value)){
                send_data(array('flag'=>false,'msg'=>'错误:collection: ['.$collection.'] add_time should be number not 【'.$value.'】'));
            }
        }
        if($filter){
            foreach($filter as $key=> $value){
                if($key=='_key'){continue;}
                else if(!in_array($key,$prop_arr)){
                    if(c('debug')){
                        send_data(array('flag'=>false,'msg'=>'错误: collection: ['.$collection.'] not have field 【'.$key.'】1'));
                    }
                    else{
                        throw new Exception('collection: ['.$collection.'] not have field 【'.$key.'】1');
                    }
                }
            }
        }

        return true;//返回成功
    }
     //插入
    protected  function  insert($collection,$insert_data=array()){
        $this->check($collection,$insert_data);
        $flag= Model2($collection)->insert($insert_data);
        return $flag;
    }
    //更新
    protected  function  update($collection,$filter=array(),$update_data=array()){
        if(!$filter){
            $this->send_data(array('flag'=>false,'msg'=>'need '));
        }
        $this->check($collection,$update_data,$filter);
        $flag= Model2($collection)->filter($filter)->update($update_data);
        return $flag;
    }
     //直接返回结果
    protected  function  update_crm($data_func){
        $arr = Model('crm')->check_id();
        $collection = $arr[0];
        $id = $arr[1];

        if(!$collection){return array('flag'=>false,'msg'=>'update_crm need collection');}
        if(!$id){return array('flag'=>false,'msg'=>'update_crm need id');}
        if(!$data_func){return array('flag'=>false,'msg'=>'update_crm update func');}
        if(is_string($data_func)){
           $insert_data= $this->$data_func();
        }
        else if(is_array($data_func)){
            $insert_data=$data_func;
        }

        if(isset($insert_data['flag'])&&$insert_data['msg']){
            return $insert_data;//不符合条件
        }
        $flag=$this->update($collection,array('_key'=>$id),$insert_data);
        return array('flag'=>$flag,'msg'=>$flag?'设置成功':'设置失败');
    }

    protected  function  insert_crm($collection,$data_func){
        if(!$collection){return array('flag'=>false,'msg'=>'update_crm update collection');}
        if(!$data_func){return array('flag'=>false,'msg'=>'update_crm update func');}

        if(is_string($data_func)){
            $insert_data= $this->$data_func();
        }
        else if(is_array($data_func)){
            $insert_data=$data_func;
        }

        if(isset($insert_data['flag'])&&$insert_data['msg']){
            return $insert_data;//不符合条件
        }
        $this->check($collection,$insert_data);

        $flag= Model2($collection)->insert($insert_data);
        return array('flag'=>$flag,'msg'=>$flag?'添加成功':'添加失败');
    }

    //同步数据数据使用
    protected  function  crm_insert_update($collection,$filter,$update,$force_update_flag=false){
        if(!is_array($update)){
            return array('flag'=>false,'msg'=>'update data should be array! plugin2.php 406');
        }
        //数字丢失问题,比如时间戳post过来后变成了字符串
        foreach($update as $pair_key=>$pair_value){
            if(is_numeric($pair_value)){
                $update[$pair_key]=floatval($pair_value);
            }
        }
        $result= Model2($collection)->filter($filter)->returns('_key:id')->find();

        if($result){

            if($force_update_flag){//如果没有明确指定不更新
                $this->update($collection,array('_key'=>$result['id']),$update);
            }
        }
        else{
            $this->insert($collection,$update);//插入和检查可用性
        }
    }

    //删除服务
    protected function  set_delete()
    {
        $arr = Model('crm')->check_id();
        $collection = $arr[0];
        $id = $arr[1];
        $flag = Model2($collection)->filter(array('_key' => $id))->delete();
        return array('flag' => $flag, 'msg' => $flag ? '删除成功' : '删除失败');
    }
}

//第二个参数代表是否从  app/plugin的格式，无app/则默认为当前app;  添加初始化即有加载配置的行为父类construct加载
function plugin2($load_plugin,$pre_callback=null){
     if(strpos($load_plugin,'/')){//包含区分组件的情况,不包括开始就是/的情况
        $arr=explode('/',$load_plugin);
         $app=$arr[0];
         $plugin=$arr[1];
         $file_name = ROOT_PATH .'/'.$app.'/plugins/' .$plugin.'/'. $plugin . '.php';
     }
    else{
        $plugin=$load_plugin;
        //先从core中找，没有的话再从本地plugin中找
        $file_name = ROOT_PATH . '/data/plugins/' .$plugin.'/'. $plugin . '.php';
        if(!file_exists($file_name)){
          die('美容自定义组件['.$load_plugin.']不存在!');
        }
        $load_plugin=basename(BASE_PATH).'/'.$plugin;//为了保证键值一致性，添加app前缀。否则可能声明两个对象
    }

    static $_cache = array();
    static $pre_hook=array();  //预挂载事件，用于初始化执行，主要解决配置只有一个位置正确，且初始化一次的问题
    if($pre_callback){
        $pre_hook[$load_plugin]=$pre_callback;
        return true;
    }
    if (isset($_cache[$load_plugin])) {
        return $_cache[$load_plugin];
    }

    if (!$plugin) //如果为空或null直接返回model对象
    {var_dump($plugin);
        throw new Exception('no plugin param find');  //没有插件对象，更容易发现错误
      //  return new Plugin($file_name);
    }
    $class_name =$plugin . 'Plugin';
    //重新生成一个新对象，已经引入过文件
    if (class_exists(@$class_name, false)) { //由于测试是indexControl已经加载过但不是这个方法加载过的，所以没在静态缓存中
        $_cache[$load_plugin] = new $class_name($file_name);
       //触发初始化函数
        if(isset($pre_hook[$load_plugin])&&$pre_hook[$load_plugin]){
            $pre_hook[$load_plugin]($_cache[$load_plugin],$plugin);
        }
        return $_cache[$load_plugin];
    }
    include($file_name); //动态引入文件
    if (!class_exists($class_name)) {
        $error = 'Plugin Error:  Plugin ' . $class_name . ' is not exists!';
        throw new Exception($error);
    } else {
        $_cache[$load_plugin] = new $class_name($file_name);
        if(isset($pre_hook[$load_plugin])&&$pre_hook[$load_plugin]){
            $pre_hook[$load_plugin]($_cache[$load_plugin],$plugin);
        }
        return  $_cache[$load_plugin];
    }
}

//判断组件是否存在
function plugin2_exist($load_plugin){
    if(strpos($load_plugin,'/')){//包含区分组件的情况,不包括开始就是/的情况
        $arr=explode('/',$load_plugin);
        $app=$arr[0];
        $plugin=$arr[1];
        $file_name = ROOT_PATH .'/data/plugins/' .$plugin.'/'. $plugin . '.php';
    }
    else{
        $plugin=$load_plugin;
        //先从core中找，没有的话再从本地plugin中找
        $file_name = CORE_PATH . '/data/plugins/' .$plugin.'/'. $plugin . '.php';
        if(!file_exists($file_name)){
            $file_name = BASE_PATH . '/data/plugins/' .$plugin.'/'. $plugin . '.php'; //向上兼容
        }
        $load_plugin=basename(BASE_PATH).'/'.$plugin;//为了保证键值一致性，添加app前缀。否则可能声明两个对象
    }
    if(file_exists($file_name)){
        return true;
    }
    else{return false;
    }
}




